package io.lvdaxian.mybatis19.executor.keygen;

import io.lvdaxian.mybatis19.executor.Executor;
import io.lvdaxian.mybatis19.executor.keygen.KeyGenerator;
import io.lvdaxian.mybatis19.mapping.MappedStatement;
import io.lvdaxian.mybatis19.reflection.MetaObject;
import io.lvdaxian.mybatis19.session.Configuration;
import io.lvdaxian.mybatis19.type.TypeHandler;
import io.lvdaxian.mybatis19.type.TypeHandlerRegistry;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * 使用 JDBC3 Statement.getGeneratedKeys
 *
 * @author lihh
 */
public class Jdbc3KeyGenerator implements KeyGenerator {
  
  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    // Do Nothing
  }
  
  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  
  }
  
  public void processBatch(MappedStatement ms, Statement stmt, List<Object> parameters) {
    try (ResultSet rs = stmt.getGeneratedKeys()) {
      final Configuration configuration = ms.getConfiguration();
      final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
      final String[] keyProperties = ms.getKeyProperties();
      final ResultSetMetaData rsmd = rs.getMetaData();
      TypeHandler<?>[] typeHandlers = null;
      if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
        for (Object parameter : parameters) {
          // there should be one row for each statement (also one for each parameter)
          if (!rs.next()) {
            break;
          }
          final MetaObject metaParam = configuration.newMetaObject(parameter);
          if (typeHandlers == null) {
            // 先取得类型处理器
            typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties);
          }
          // 填充键值
          populateKeys(rs, metaParam, keyProperties, typeHandlers);
        }
      }
    } catch (Exception e) {
      throw new RuntimeException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
    }
  }
  
  private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties) {
    TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
    for (int i = 0; i < keyProperties.length; i++) {
      if (metaParam.hasSetter(keyProperties[i])) {
        Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
        TypeHandler<?> th = typeHandlerRegistry.getTypeHandler(keyPropertyType, null);
        typeHandlers[i] = th;
      }
    }
    return typeHandlers;
  }
  
  private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
    for (int i = 0; i < keyProperties.length; i++) {
      TypeHandler<?> th = typeHandlers[i];
      if (th != null) {
        Object value = th.getResult(rs, i + 1);
        metaParam.setValue(keyProperties[i], value);
      }
    }
  }
}
